﻿/* MVT Charts for Silverlight - Version 1
 * 2008 by Mario Meir-Huber
 * mailto: i-mameir@microsoft.com
 *         mario@meirhuber.de
 */ 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

/*
 * ChartLibrary.Base is the main Namespace for charts
 */ 
namespace ChartLibrary.Base
{
    /// <summary>
    /// an AxisChart is the Base for Linecharts, Areacharts and
    /// Column- and Barcharts. Or so to say: all charts, using Axises
    /// </summary>
    public class AxisCharts : ChartControl
    {

        private bool _displayaxislabel;         //displays axis label
        private bool _displayaxisnumbers;       //display axis numbers

        private string _ylabel;                 //Y Axis Label
        private string _xlabel;                 //X Axis Label

        private int _xaxislines = 0;            //X Axis supporting Lines
        private int _yaxislines = 0;            //Y Axis supporting Lines

        private bool LayoutChangedHandled = false; //indicates that no layout update is required

        /// <summary>
        /// Calculation Areas
        /// </summary>
        private double _areaXstart, _areaYstart, _areaWidth, _areaHeight;

        /// <summary>
        /// Base for a Line Chart
        /// </summary>
        /// <param name="parent"></param>
        public AxisCharts()
        {
            this.SizeChanged += new SizeChangedEventHandler(LinesChart_SizeChanged);
            this.DrawAreaChanged += new StateChangedEvent(AxisCharts_LayoutChanged);
        }

        /// <summary>
        /// Occurs when the Layout was changed
        /// </summary>
        void AxisCharts_LayoutChanged()
        {
            if (!LayoutChangedHandled) 
            UpdateChartControl();

            LayoutChangedHandled = false;
        }

        /// <summary>
        /// occurs when the Size of the control was changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void LinesChart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateChartControl();
        }

        /// <summary>
        /// get a boolean variable indicating if the axis labels will be displayed or sets it
        /// </summary>
        public bool DisplayAxisLabel
        {
            get
            {
                return _displayaxislabel;
            }
            set
            {
                if (_displayaxislabel == value) return;

                _displayaxislabel = value;

                if (!value) RemoveAxisLabel();

                UpdateChartControl();
            }
        }

        /// <summary>
        /// gets a boolean value indicating if the axis numbers will be displayed or sets it
        /// </summary>
        public bool DisplayAxisNumbers
        {
            get
            {
                return _displayaxisnumbers;
            }
            set
            {
                if (_displayaxisnumbers == value) return;

                if (!value) RemoveAxisNumbers();

                _displayaxisnumbers = value;

                UpdateChartControl();
            }
        }

        /// <summary>
        /// gets the text for the Y Axis or sets it
        /// </summary>
        public string YLabel
        {
            get
            {
                return _ylabel;
            }
            set
            {
                TextBlock txt = LayoutRoot.FindName("_axislabel_y") as TextBlock;
                _ylabel = value;

                if (null != txt) txt.Text = value;
            }
        }

        /// <summary>
        /// gets the text for the X Axis or sets it
        /// </summary>
        public string XLabel
        {
            get
            {
                return _xlabel;
            }
            set
            {
                TextBlock txt = LayoutRoot.FindName("_axislabel_x") as TextBlock;
                _xlabel = value;

                if (null != txt) txt.Text = value;
            }
        }

        /// <summary>
        /// removes the Axis Labels if they are no longer needed
        /// </summary>
        private void RemoveAxisLabel()
        {
            LayoutRoot.Children.Remove(LayoutRoot.FindName("_axislabel_x") as TextBlock);
            LayoutRoot.Children.Remove(LayoutRoot.FindName("_axislabel_y") as TextBlock);
        }

        /// <summary>
        /// removes the Axis Number Labels if they are no longer needed
        /// </summary>
        private void RemoveAxisNumbers()
        {
            for (int i = 1; i < 4; i++) LayoutRoot.Children.Remove(LayoutRoot.FindName("_axisvalue_y" + i.ToString()) as TextBlock);
            for (int i = 1; i < 3; i++) LayoutRoot.Children.Remove(LayoutRoot.FindName("_axisvalue_x" + i.ToString()) as TextBlock);
        }

        /// <summary>
        /// updated and redraws the chart control
        /// </summary>
        private void UpdateChartControl()
        {
            TextBlock tmptext;
            Line tmpline;
            Transform tmptransform;

            double startx = 0.0, starty = 0.0; //startx = where x-axis can start; starty = where y-axis can start
            double tmpmost = 0.0;

            double tmpcalcwidth = 0.0;

            if (ActualHeight == 0.0 || ActualWidth == 0.0) return;

            if (_displayaxislabel)
            {
                //Place or replace y axis label
                tmptext = LayoutRoot.FindName("_axislabel_y") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();
                    tmptext.SetValue(Canvas.NameProperty, "_axislabel_y");
                    tmptext.Text = _ylabel;

                    tmptransform = new RotateTransform();
                    tmptransform.SetValue(RotateTransform.AngleProperty, 270.0);
                    tmptransform.SetValue(RotateTransform.CenterXProperty, 0.5);
                    tmptransform.SetValue(RotateTransform.CenterYProperty, 0.5);
                    tmptext.RenderTransform = tmptransform;


                    LayoutRoot.Children.Add(tmptext);

                }

                starty = tmptext.ActualHeight + 5.0;
                tmptext.Foreground = Foreground;

                tmptext.SetValue(Canvas.TopProperty, (base.AreaStartY + ActualHeight + tmptext.ActualWidth / 2) / 2);
                tmptext.SetValue(Canvas.LeftProperty, base.AreaStartX);

                //Place or replace x axis label
                tmptext = LayoutRoot.FindName("_axislabel_x") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();
                    tmptext.SetValue(Canvas.NameProperty, "_axislabel_x");
                    tmptext.Text = _xlabel;
                    LayoutRoot.Children.Add(tmptext);
                }

                tmptext.Foreground = Foreground;
                startx = tmptext.ActualHeight;

                tmptext.SetValue(Canvas.LeftProperty, ((base.AreaWidth - base.AreaStartX) - tmptext.ActualWidth) / 2);
                tmptext.SetValue(Canvas.TopProperty, base.AreaHeight + base.AreaStartY - tmptext.ActualHeight);
            }

            //displays the axis numbers
            if (_displayaxisnumbers)
            {
                tmptext = LayoutRoot.FindName("_axisvalue_y1") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();
                    tmptext.Text = Data.Max.ToString();
                    tmptext.SetValue(Canvas.NameProperty, "_axisvalue_y1");
                    LayoutRoot.Children.Add(tmptext);
                }

                tmptext.SetValue(Canvas.LeftProperty, starty + 5.0);
                tmptext.SetValue(Canvas.TopProperty, AreaStartY);
                tmptext.Foreground = Foreground;

                if (tmpmost < tmptext.ActualWidth) tmpmost = tmptext.ActualWidth;

                tmptext = LayoutRoot.FindName("_axisvalue_y2") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();
                    tmptext.Text = (Data.Max - (Data.Max - Data.Min) / 2).ToString();
                    tmptext.SetValue(Canvas.NameProperty, "_axisvalue_y2");
                    LayoutRoot.Children.Add(tmptext);
                }

                tmptext.SetValue(Canvas.LeftProperty, starty + 5.0);
                tmptext.Foreground = Foreground;

                if (tmpmost < tmptext.ActualWidth) tmpmost = tmptext.ActualWidth;


                tmptext = LayoutRoot.FindName("_axisvalue_y3") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();
                    tmptext.Text = Data.Min.ToString();
                    tmptext.SetValue(Canvas.NameProperty, "_axisvalue_y3");
                    LayoutRoot.Children.Add(tmptext);
                }

                tmptext.SetValue(Canvas.LeftProperty, starty + 5.0);
                tmptext.Foreground = Foreground;

                if (tmpmost < tmptext.ActualWidth) tmpmost = tmptext.ActualWidth;

                tmpmost += 10.0;

                tmptext = LayoutRoot.FindName("_axisvalue_x1") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();

                    if (Data.Count > 0)
                        if (Data[0].Length > 0)
                            tmptext.Text = Data[0][0].XAxisValue;

                    tmptext.SetValue(Canvas.NameProperty, "_axisvalue_x1");
                    LayoutRoot.Children.Add(tmptext);
                }

                startx += tmptext.ActualHeight;

                tmptext.SetValue(Canvas.LeftProperty, base.AreaStartX + starty + tmpmost);
                tmptext.SetValue(Canvas.TopProperty, base.AreaHeight + base.AreaStartY - startx);
                tmptext.Foreground = Foreground;

                tmptext = LayoutRoot.FindName("_axisvalue_x2") as TextBlock;

                if (null == tmptext)
                {
                    tmptext = new TextBlock();

                    if (Data.Count > 0)
                        if (Data[0].Length > 0)
                            tmptext.Text = Data[0][Data[0].Length - 1].XAxisValue;

                    tmptext.SetValue(Canvas.NameProperty, "_axisvalue_x2");
                    LayoutRoot.Children.Add(tmptext);
                }
                tmptext.Foreground = Foreground;

                tmptext.SetValue(Canvas.LeftProperty, (base.AreaStartX + base.AreaWidth) - tmptext.ActualWidth);
                tmptext.SetValue(Canvas.TopProperty, base.AreaStartY + base.AreaHeight - startx);

                tmptext = LayoutRoot.FindName("_axisvalue_y2") as TextBlock;
                tmptext.SetValue(Canvas.TopProperty, (base.AreaHeight + base.AreaStartY - startx - tmptext.ActualHeight) / 2);

                tmptext = LayoutRoot.FindName("_axisvalue_y3") as TextBlock;
                tmptext.SetValue(Canvas.TopProperty, base.AreaHeight + base.AreaStartY - startx - tmptext.ActualHeight);

            }

            //Displays the Axis
            tmpline = LayoutRoot.FindName("_axisliney") as Line;

            if (null == tmpline)
            {
                tmpline = new Line();
                tmpline.SetValue(Canvas.NameProperty, "_axisliney");
                LayoutRoot.Children.Add(tmpline);
            }
            tmpline.Fill = Foreground;

            tmpline.Stroke = Foreground;
            tmpline.StrokeThickness = 1;

            tmpline.X1 = base.AreaStartX + starty + tmpmost;
            tmpline.X2 = tmpline.X1;

            tmpline.Y1 = base.AreaStartY;
            tmpline.Y2 = base.AreaHeight + base.AreaStartY - startx;

            _areaYstart = base.AreaStartY;
            _areaHeight = tmpline.Y2 - tmpline.Y1;

            tmpline = LayoutRoot.FindName("_axislinex") as Line;

            if (null == tmpline)
            {
                tmpline = new Line();
                tmpline.SetValue(Canvas.NameProperty, "_axislinex");

                LayoutRoot.Children.Add(tmpline);
            }

            tmpline.Stroke = Foreground;
            tmpline.StrokeThickness = 1;

            tmpline.X1 = base.AreaStartX + starty + tmpmost;
            tmpline.X2 = base.AreaWidth;

            tmpline.Y1 = base.AreaHeight + base.AreaStartY - startx;
            tmpline.Y2 = tmpline.Y1;

            _areaXstart = tmpline.X1;
            _areaWidth = tmpline.X2 - tmpline.X1;

            //calculates the metrics
            _areaWidth = base.AreaWidth;

            //displays the x and y axis helper
            if (_xaxislines > 0)
            {
                tmpcalcwidth = (base.AreaWidth - starty) / (_xaxislines + 1);

                for (int i = 0; i < _xaxislines; i++)
                {
                    tmpline = LayoutRoot.FindName("_axishelper_x" + i.ToString()) as Line;

                    if (null == tmpline)
                    {
                        tmpline = new Line();

                        tmpline.SetValue(Canvas.NameProperty, "_axishelper_x" + i.ToString());
                        tmpline.Stroke = new SolidColorBrush(Color.FromArgb(70, 0, 0, 0));
                        tmpline.StrokeThickness = 1;

                        LayoutRoot.Children.Add(tmpline);
                    }

                    tmpline.X1 = base.AreaStartX + starty + tmpmost + tmpcalcwidth * (i + 1);
                    tmpline.X2 = tmpline.X1;

                    tmpline.Y1 = base.AreaStartY;
                    tmpline.Y2 = base.AreaHeight + base.AreaStartY - startx;
                }
            }

            if (_yaxislines > 0)
            {
                tmpcalcwidth = (base.AreaHeight - startx) / (_yaxislines + 1);

                for (int i = 0; i < _yaxislines; i++)
                {
                    tmpline = LayoutRoot.FindName("_axishelper_y" + i.ToString()) as Line;

                    if (null == tmpline)
                    {
                        tmpline = new Line();

                        tmpline.SetValue(Canvas.NameProperty, "_axishelper_y" + i.ToString());
                        tmpline.Stroke = new SolidColorBrush(Color.FromArgb(70, 0, 0, 0));
                        tmpline.StrokeThickness = 1;

                        LayoutRoot.Children.Add(tmpline);
                    }

                    tmpline.X1 = base.AreaStartX + starty + tmpmost;
                    tmpline.X2 = base.AreaWidth - base.AreaStartX;

                    tmpline.Y1 = base.AreaStartY + tmpcalcwidth * (i + 1);
                    tmpline.Y2 = tmpline.Y1;
                }
            }

            Area.Height = _areaHeight;
            Area.Width = _areaWidth;
            Area.StartX = _areaXstart;
            Area.StartY = _areaYstart;

            LayoutChangedHandled = true;
            base.RaiseAreaChanged();
        }

        /// <summary>
        /// Displays Lines on the X Axis
        /// </summary>
        public int XAxisLines
        {
            get
            {
                return _xaxislines;
            }
            set
            {
                if (value == _xaxislines) return;

                if (value < _xaxislines) RemoveXAxisLines();

                _xaxislines = value;

                UpdateChartControl();
            }
        }

        /// <summary>
        /// Removes the Axis lines, if they are no longer needed ... 
        /// </summary>
        private void RemoveXAxisLines()
        {
            for (int i = 0; i < _xaxislines; i++)
            {
                LayoutRoot.Children.Remove(LayoutRoot.FindName("_axishelper_x" + i.ToString()) as Line);
            }
        }

        /// <summary>
        /// Displays Lines on the Y Axis
        /// </summary>
        public int YAxisLines
        {
            get
            {
                return _yaxislines;
            }
            set
            {
                if (value == _yaxislines) return;

                if (value < _yaxislines) RemoveYAxisLines();

                _yaxislines = value;

                UpdateChartControl();
            }
        }

        /// <summary>
        /// removes the y axis lines, if they are no longer needed 
        /// </summary>
        private void RemoveYAxisLines()
        {
            for (int i = 0; i < _yaxislines; i++)
            {
                LayoutRoot.Children.Remove(LayoutRoot.FindName("_axishelper_y" + i.ToString()) as Line);
            }
        }
    }
}
